#include<cassert>
#include<cerrno>
#include<cstring>
#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<thread>
#include<time.h>

using namespace std;


#define THREAD_NUM 5

class ThreadData
{
public:
    ThreadData(const string &n, pthread_mutex_t *pm) : tname(n), pmtx(pm)
    {}
public:
    string tname;
    pthread_mutex_t *pmtx;
};


//如果多线程访问同一个全局变量，并对它进行数据计算，多线程会互相影响吗
//加锁保护：加锁的时候，一定要保证加锁的粒度，越小越好
//定义的锁是静态或者全局的用以下方式对锁进行初始化
//pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;//pthread_mutex_t 就是原生线程库提供的数据类型

//加锁就是串行了吗？加锁之后，线程在临界区中，是否会切换，会有问题吗？原子性的体现
//会切换，不会！第一次理解：虽然被切换了，但是你是持有锁被切换的，所以其他抢票线程要执行临界区代码，也必须先申请锁，锁他是无法申请成功的，所以，你也不会让其他线程进去临界区，就保证了临界区数据的一致性！
//我是一个线程，我不申请锁，就是单纯的访问临界资源---这是一种错误的编码方式
//在没有持有锁的线程看来，对我最有意义的情况只有2种：1.线程1没有持有锁(什么都没做) 2.线程1释放锁(做完)，此时我可以申请锁
//加了锁就是串行执行吗？是的，执行临界区的代码一定是串行的
//要访问临界资源，每一个线程都必须先申请锁，每一个线程都必须先看到同一把锁&&访问它，锁本身就是一种共享资源
//谁来保证锁的安全性？所以为了保证锁的安全，申请和释放锁，必须是原子的！自己保证
//如何保证？锁是如何实现的？  

int tickets = 10000; //在并发访问的时候，导致了我们数据不一致的问题

void *getTickets(void *args)
{
    ThreadData *td = (ThreadData*)args;
    while(true)
    {
        //给线程加锁，任意时刻只允许一个线程执行，(互斥，串行)
        int n = pthread_mutex_lock(td->pmtx);
        assert(n == 0);
        //临界区
        if(tickets > 0) //1.判断的本质是计算的一种
        {
            usleep(rand()%1500);
            printf("%s: %d\n",td->tname.c_str(),tickets);
            tickets--; //2.也可能出现问题
            n = pthread_mutex_unlock(td->pmtx);
            assert(n == 0);
        }
        //解锁
        else
        { 
            n = pthread_mutex_unlock(td->pmtx);
            assert(n == 0);
            break;
        }

        //抢完票，其实还需后续的动作 
        usleep(rand()%2000);
    }
    delete td;
    return nullptr;
}



int main()
{
    time_t start = time(nullptr);
    //定义的锁为局部变量使用pthread_mutex_init(&mtx,nullptr);初始化;    释放： pthread_mutex_destroy(&mtx);
    pthread_mutex_t mtx;
    pthread_mutex_init(&mtx,nullptr);
    srand((unsigned long)time(nullptr)^getpid()^0x45);
    pthread_t t[THREAD_NUM];
    // pthread_t t1,t2,t3;
    //多线程抢票的逻辑
    for(int i = 0; i < 5; i++)
    {
        string name = "thread ";
        name += to_string(i+1);

        ThreadData *td = new ThreadData(name, &mtx);
        pthread_create(t+i,nullptr,getTickets, (void*)td);
    }

    for(int i = 0; i < THREAD_NUM; i++)
    {
        pthread_join(t[i],nullptr);
    }

    // pthread_create(&t1,nullptr,getTickets,(void*)"thread one");
    // pthread_create(&t2,nullptr,getTickets,(void*)"thread two");
    // pthread_create(&t3,nullptr,getTickets,(void*)"thread three");

    // pthread_join(t1,nullptr);
    // pthread_join(t2,nullptr);
    // pthread_join(t3,nullptr);

    pthread_mutex_destroy(&mtx);

    time_t end = time(nullptr);

    cout << "cast: " << (int)(end - start) << endl;
}






// void fun()
// {
//     while(true)
//     {
//         cout << "hello new thread" << endl;
//         sleep(1);
//     }
// }

// int main()
// {
//     thread t(fun);
//     thread t1(fun); 
//     thread t2(fun);
//     thread t3(fun);
//     thread t4(fun);

//     while(true)
//     {
//         cout << "hello main thread" << endl;
//         sleep(1);
//     }

//     t.join();
// }







// //全局区变量，下面的代码可以看见，主线程和新线程运行的结果一样，得出结论，线程的全局区是共享的
// //int g_val = 0;

// // _ _thread : 修饰全局变量，带来的结果就是让每一个线程各自拥有一个全局的变量 -- 线程的局部存储
// __thread int g_val = 0;

// //1.线程谁先运行与调度器有关
// //2.线程一旦异常，都可能导致整个进程整体退出
// //3.线程的输入和返回值问题
// //4.线程异常退出的理解，主线程main thread不关心新线程是否异常，只要活着就是最好的结果，如果新线程异常了，进程退出
// void *threadRoutine(void *args)
// {
//     pthread_detach(pthread_self());

//     // sleep(5);
//     // //进程替换,一旦替换相当于除了主线程外，其他所有线程都终止，终止后让整个进程执行替换后的程序
//     // execl("/bin/ls","ls",nullptr);
//     while(true)
//     {
//         cout << (char*)args << " : " << (int)g_val <<"  &: "<< &g_val << endl;
//         g_val++;
//         sleep(1); 
//     }
//     pthread_exit((void*)11); 

//     // //自己取消自己，不推荐这么做
//     // // pthread_cancel(pthread_self());
//     // int i = 0;
//     // // int *data = new int[10];
//     // while(true)
//     // {
//     //     //pthread_self哪个线程调用我，我获取的就是哪个线程的id
//     //     cout << "新线程：" << (char*)args << " running ..." << pthread_self() << endl;
//     //     sleep(1);
//     //     // data[i] = i;
//     //     // if(i++ == 3) break;

//     //     // int a = 100;
//     //     // a /= 0;
//     // }

//     // //pthread_exit((void*)13); //线程终止
//     // //exit(10); //不要调用exit，exit是终止进程的！
//     // cout << "new thread quit..." << endl;
//     // //return (void*)data;
//     // // return (void*)10; //是返回给谁呢？一般是给main thread,那么主线程如何获取到呢？主线程用pthread_join获取
// }

// int main()
// {
//     //因为我们目前用的不是Linux自带的创建线程的接口，我们用的是pthread库中的接口
//     pthread_t tid;//本质是一个地址
//     pthread_create(&tid,nullptr,threadRoutine,(void*)"thread 1");

//     while(true)
//     {
//         cout << "main thread: " << (int)g_val << "  &: "<< &g_val << endl;
//         sleep(1);
//         break;
//     }
//     //线程分离后，主线程不需要管了，也不能管了，如果非要管呢?报错！
//     // int n = pthread_join(tid,nullptr);
//     // cout << "n: " << (int)n << "  errstring；" << strerror(n) << endl;



//     // printf("%lu,%p\n",tid,tid);
//     // int count = 0;
//     // while(true)
//     // {
//     //     cout << "main线程：" << " running ..." << pthread_self() << endl;
//     //     sleep(1);
//     //     count++;
//     //     if(count >= 5) break;
//     // }
//     // pthread_cancel(tid);
//     // cout << "pthread cancel : " << tid << endl;

//     // //int pthread_join(pthread_t thread, void **retval);
//     // int *ret = nullptr;
//     // pthread_join(tid,(void**)&ret); //默认会阻塞等待新线程退出
//     // //1.线程被取消，join的时候，退出码是-1， #define PTHREAD_CANCELED ((void*)-1)
//     // //PHTREAD_CANCELED;
//     // cout << "main thread wait done ... main quit...: new thred quit: " << (long int)ret << "\n";
//     // sleep(5);
//     // for(int i = 0; i < 10; ++i)
//     // {
//     //     cout << ret[i] << endl;
//     // }

//     //3.线程再光剑并执行的时候，线程也是需要进行等待的，如果主线程不等待，既会引起类似于进程的僵尸问题，导致内存泄漏
//     // while(true)
//     // {
//     //     cout << "main线程：" << " running ..." << endl;
//     //     sleep(1);
//     // }
//     return 0;
// }